﻿//AcademicPlanner - course registration planning web app.
//Copyright (C) 2009  Boubacar Diallo

//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either version 2
//of the License, or (at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AcademicPlanner.Data;
using AcademicPlanner.Data.Entities;
using AcademicPlanner.Data.Repositories;
using AcademicPlanner.Domain.Models.Collections;
using AcademicPlanner.Framework.Libraries.Validation;
using AcademicPlanner.Framework.Helpers;

namespace AcademicPlanner.Domain.Models
{
	/// <summary>
	/// CourseDivision represents a top level hierarchy in the organization
	/// and grouping of academic courses.
	/// </summary>
	/// <since>1.0</since>
	public class CourseDivision : Model
	{
		/// <summary>
		/// The <c>AcademicPlan</c> this CourseDivision belongs to.
		/// </summary>
		/// <since>1.0</since>
		protected AcademicPlan plan;

		/// <summary>
		/// The collection of <c>CourseCategory</c>s under this CourseDivision.
		/// </summary>
		/// <since>1.0</since>
		protected CourseCategoryCollection categories;


		/// <summary>
		/// Gets the data layer repository for this CourseDivision's underlying data representation.
		/// </summary>
		/// <since>1.0</since>
		protected override EntityRepository EntityRepo { get { return DomainMaster.DSAS.CourseDivisionEntityRepo; } }

		/// <summary>
		/// Gets this CourseDivision's underlying data representation.
		/// </summary>
		/// <since>1.0</since>
		protected CourseDivisionEntity Entity { get { return (CourseDivisionEntity)entity; } }

		/// <summary>
		/// Gets this CourseDivision's name.
		/// </summary>
		/// <since>1.0</since>
		public string Name
		{
			get
			{
				return Entity.Name;
			}
		}

		/// <summary>
		/// Gets the collection of <c>CourseCategory</c>s under this CourseDivision.
		/// </summary>
		/// <since>1.0</since>
		public CourseCategoryCollection Categories 
		{
			get
			{
				SetCategories();
				return categories; 
			} 
		}

		/// <summary>
		/// Gets the <c>AcademicPlan</c> this CourseDivision belongs to.
		/// </summary>
		/// <since>1.0</since>
		public AcademicPlan Plan
		{
			get
			{
				SetPlan();
				return plan;
			}
		}


		/// <summary>
		/// Constructs a new CourseDivision.
		/// </summary>
		/// <remarks>
		/// This constructor invokes the matching super constructor.
		/// </remarks>
		/// <since>1.0</since>
		public CourseDivision()
			: base()
		{
			
		}

		/// <summary>
		/// Constructs a new CourseDivision with the provided <c>CourseDivisionEntity</c>.
		/// </summary>
		/// <remarks>
		/// This constructor invokes the matching super constructor.
		/// </remarks>
		/// <param name="pEntity">The CourseDivision's <c>CourseDivisionEntity</c>.</param>
		/// <since>1.0</since>
		public CourseDivision(CourseDivisionEntity pEntity) 
			: base(pEntity)
		{
			
		}


		private void ResetPlan()
		{
			if (Entity.PlanEntity != null)
				plan = DomainMaster.ModelsFountain.GetAcademicPlan(Entity.PlanEntity);
		}

		private void SetPlan()
		{
			if (plan == null)
				ResetPlan();
		}

		private void ResetCategories()
		{
			categories = new CourseCategoryCollection(Entity.CategoryEntities);
		}

		private void SetCategories()
		{
			if (categories == null)
				ResetCategories();
		}

		internal void Set(string pName)
		{
			var vErrs = Validate(pName);

			if (vErrs.OuterErrors.Count > 0)
				throw new InvalidModelPropertiesException(DomainErrorCode.M_INVALID_COURSEDIVISION_PROPERTIES, vErrs);

			SetPlan();
			if (plan != null)
			{
				vErrs = plan.ValidateDivision(entity.Id, pName ?? "");
				if (vErrs.OuterErrors.Count > 0)
					throw new InvalidModelPropertiesException(DomainErrorCode.M_INVALID_COURSEDIVISION_PROPERTIES, vErrs);
			}

			if (pName != null)
				Entity.Name = SecurityHelper.SanitizeInput(pName);
		}

		/// <summary>
		/// Validates possible values of a Program's properties. <c>null</c> values are ignored.
		/// </summary>
		/// <remarks>
		/// <c>String</c> inputs are sanitized first.
		/// </remarks>
		/// <param name="pName">The name.</param>
		/// <returns>A Collection of validation errors.</returns>
		/// <seealso cref="M:ValidateName"/>
		/// <seealso cref="M:AcademicPlanner.Framework.Helpers.SecurityHelper.SanitizeInput"/>
		/// <since>1.0</since>
		public static InvalidModelPropertiesErrorCollection Validate(string pName)
		{
			var rVal = new InvalidModelPropertiesErrorCollection("CourseDivision");
			InvalidModelPropertyErrorCode vCode;

			if (pName != null)
			{
				if ((vCode = ValidateName(SecurityHelper.SanitizeInput(pName))) != InvalidModelPropertyErrorCode.NONE)
					rVal.AddOuter("Name", vCode);
			}

			return rVal;
		}

		/// <summary>
		/// Validates a possible value of a CourseDivision's name.
		/// </summary>
		/// <remarks>
		/// The value must not be blank.
		/// </remarks>
		/// <param name="pVal">The value to validate.</param>
		/// <returns>A validation error code.</returns>
		/// <seealso cref="M:AcademicPlanner.Framework.Libraries.Validation.StringValidator.IsNotBlank"/>
		/// <since>1.0</since>
		public static InvalidModelPropertyErrorCode ValidateName(string pVal)
		{
			var rVal = InvalidModelPropertyErrorCode.NONE;

			if (!StringValidator.IsNotBlank(pVal))
				rVal = InvalidModelPropertyErrorCode.REQUIRED;

			return rVal;
		}

		internal void RemoveCategory(int pIndex)
		{
			//Plan.RemoveCoursesWithCategory(this);
			Entity.CategoryEntities[pIndex].Trash();
		}

		internal void AddCategory(CourseCategory pCategory)
		{
			var vCatErrs = ValidateCategory(pCategory.EntityId, pCategory.Name, pCategory.Code);
			var vEnt = (CourseCategoryEntity)GetOtherModelEntity(pCategory);

			if (vCatErrs.OuterErrors.Count > 0)
			{
				var vErrs = new InvalidModelPropertiesErrorCollection("CourseDivision");

				vErrs.AddInner("Categories", vCatErrs);
				throw new InvalidModelPropertiesException(DomainErrorCode.M_INVALID_COURSEDIVISION_CATEGORIES_PROPERTIES, vErrs);
			}

			Entity.AddCategoryEntity(vEnt);
			ResetCategories();
		}

		internal InvalidModelPropertiesErrorCollection ValidateCategory(int pCategoryId, string pCategoryName,
																		string pCategoryCode)
		{
			var rErrs = new InvalidModelPropertiesErrorCollection("CourseCategory");

			SetCategories();
			var vCats = categories.ToList();

			if (!pCategoryName.Equals(string.Empty)
				&& vCats.Where(pCat => (pCat.Name.ToLower().Equals(pCategoryName.ToLower())
											&& pCat.EntityId != pCategoryId)).Count() > 0)
			{
				rErrs.AddOuter("Name", InvalidModelPropertyErrorCode.NOT_UNIQUE);
			}

			if (!pCategoryCode.Equals(string.Empty) 
				&&vCats.Where(pCat => (pCat.Code.ToLower().Equals(pCategoryCode.ToLower())
											&& pCat.EntityId != pCategoryId)).Count() > 0)
			{
				rErrs.AddOuter("Code", InvalidModelPropertyErrorCode.NOT_UNIQUE);
			}

			return rErrs;
		}
	}
}
